package unit.entity.basic;

import static org.junit.Assert.*;

import java.util.ArrayList;
import java.util.Random;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import am.bll.entity.basic.Money;
import am.exception.ArgumentException;
import am.exception.ArgumentNullException;
import am.exception.InvalidOperationException;

public class MoneyTester
{
    private Random randomSeed = new Random();

    @Before
    public void setUp() throws Exception
    {
    }

    @After
    public void tearDown() throws Exception
    {
    }

    @SuppressWarnings("rawtypes")
    private void testConstructorException(String value, Class className)
    {
        try
        {
            Money m = new Money(value);
            fail("m=" + m.toString() + "   但是m应该抛出异常");
        }
        catch (Exception e)
        {
            assertTrue(className == e.getClass());
        }

    }

    private void testAddException(Money m, Money addend, Class className)
    {
        try
        {
            Money result = m.add(addend);
            fail("result=" + result.toString() + "   但是result应该抛出异常");
        }
        catch (Exception e)
        {
            assertTrue(className == e.getClass());
        }
    }

    // 获取一个指定范围内的随机数
    private int getRandomInt(int min, int max)
    {
        return min + randomSeed.nextInt(max - min + 1);
    }

    // 获取一个随机数字串
    private String getRandomValue()
    {
        // 保证返回值在区间范围内
        String part0 = getRandomInt(0, 1) == 0 ? "+" : "-";
        String part1 = String.valueOf(getRandomInt(0, 999999));
        String part2 = "";
        for (int i = 0; i < 4; i++)
        {
            part2 += getRandomInt(0, 9);
        }

        return part0 + part1 + "." + part2;
    }

    @Test
    public void testConstructor()
    {
        // 测试用例主要包括以下内容:
        // a.合法值
        //   1 一般: 整数, 精度范围内小数, 精度范围外小数, 带空格. (包括正负)
        //   2 边界: 0及等价的小数字符串, 范围内最大值及等价表示 . (包括正负)
        // b.非法值
        //   1 一般: 绝对值很大的数, 非法字符(中文,非法英文符号,中间有空格), 小数点使用错误, 正负号使用错误
        //   2 边界: null和空字符串, 超过边界值的数

        //
        // 正确的:一般:整数
        //
        Money a1101 = new Money("5");
        assertEquals("5.0000", a1101.toString(4));

        Money a1102 = new Money("13");
        assertEquals("13", a1102.toString(0));

        Money a1103 = new Money("1989");
        assertEquals("1989.00", a1103.toString(2));

        Money a1104 = new Money("008765805");
        assertEquals("8765805.0000", a1104.toString(4));

        Money a1105 = new Money("+1235");
        assertEquals("1235", a1105.toString(0));

        Money a1106 = new Money("-934");
        assertEquals("-934", a1106.toString(0));

        Money a1107 = new Money("-109");
        assertEquals("-109.0000", a1107.toString(4));

        //
        // 正确的:一般:精度范围内小数
        //
        Money a1201 = new Money("0.9");
        assertEquals("0.9000", a1201.toString(4));

        Money a1202 = new Money("11.1119");
        assertEquals("11.1", a1202.toString(1));

        Money a1203 = new Money("0099.9999");
        assertEquals("100.000", a1203.toString(3));

        Money a1204 = new Money("56.12");
        assertEquals("56.1200", a1204.toString(4));

        Money a1205 = new Money("19.0961");
        assertEquals("19.096", a1205.toString(3));

        Money a1206 = new Money("+0934.0012");
        assertEquals("934.00", a1206.toString(2));

        Money a1207 = new Money("-451034.1");
        assertEquals("-451034.1", a1207.toString(1));

        Money a1208 = new Money("-1111.1111");
        assertEquals("-1111.1111", a1208.toString(4));

        //
        // 正确的:一般:精度范围外小数
        //
        Money a1301 = new Money("00.00009923");
        assertEquals("0.0001", a1301.toString(4));

        Money a1302 = new Money("1032.123632");
        assertEquals("1032.1236", a1302.toString(4));

        Money a1303 = new Money("0099.999999999");
        assertEquals("100.00", a1303.toString(2));

        Money a1304 = new Money("+03.987654");
        assertEquals("3.9877", a1304.toString(4));

        Money a1305 = new Money("-98.75432");
        assertEquals("-98.8", a1305.toString(1));

        Money a1306 = new Money("-785434.24756373");
        assertEquals("-785434.2476", a1306.toString(4));

        //
        // 正确的:一般:带空格
        //
        Money a1401 = new Money("            999901                                 ");
        assertEquals("999901", a1401.toString(0));

        Money a1402 = new Money("           0.00123456             ");
        assertEquals("0.0012", a1402.toString(4));

        Money a1403 = new Money("    -0.9345 ");
        assertEquals("-0.9345", a1403.toString(4));

        Money a1404 = new Money("        +11.234577      ");
        assertEquals("11.2346", a1404.toString(4));

        //
        // 正确的:边界:0及等价表示
        //
        Money a2101 = new Money("0");
        assertEquals("0", a2101.toString(0));

        Money a2102 = new Money("00.000");
        assertEquals("0.0000", a2102.toString(4));

        Money a2103 = new Money("0.0000192");
        assertEquals("0.0000", a2103.toString(4));

        Money a2104 = new Money("0.00019023");
        assertEquals("0.000", a2104.toString(3));

        Money a2105 = new Money("+0");
        assertEquals("0.0000", a2105.toString(4));

        Money a2106 = new Money("-0.0");
        assertEquals("-0.0000", a2106.toString(4));

        Money a2107 = new Money("-0.00004999999");
        assertEquals("-0.0000", a2107.toString(4));

        //
        // 正确的:边界:范围内最大值及等价表示
        //
        Money a2201 = new Money(Money.Max.toString(4) + "1923");
        assertEquals(Money.Max.toString(4), a2201.toString(4));

        Money a2202 = new Money(Money.Max.toString(0) + ".499999");
        assertEquals(Money.Max.toString(0) + ".50", a2202.toString(2));

        Money a2203 = new Money(Money.Min.toString(4) + "49999");
        assertEquals(Money.Min.toString(4), a2203.toString(4));

        //
        // 错误的:一般:非法字符(中文,英文符号,中间有空格)
        //
        testConstructorException("中文12.90", ArgumentException.class);
        testConstructorException("139.0中文", ArgumentException.class);

        testConstructorException("oo0.123", ArgumentException.class);
        testConstructorException("12.!23", ArgumentException.class);
        testConstructorException("abcIlov", ArgumentException.class);
        testConstructorException("12z", ArgumentException.class);
        testConstructorException("a903.1 ", ArgumentException.class);
        testConstructorException("(9.34)", ArgumentException.class);

        testConstructorException("  1. 1234 ", ArgumentException.class);
        testConstructorException("12 2", ArgumentException.class);

        //
        // 错误的:一般:小数点使用错误
        //
        testConstructorException(".120", ArgumentException.class);
        testConstructorException("934.", ArgumentException.class);
        testConstructorException("..123", ArgumentException.class);
        testConstructorException("1235..45", ArgumentException.class);
        testConstructorException("934..", ArgumentException.class);

        //
        // 错误的:一般:正负号使用错误
        //
        testConstructorException("++123.3", ArgumentException.class);
        testConstructorException("--123.3", ArgumentException.class);
        testConstructorException("+-123.3", ArgumentException.class);
        testConstructorException("-+123.3", ArgumentException.class);
        testConstructorException("123.3+", ArgumentException.class);
        testConstructorException("123.3-", ArgumentException.class);
        testConstructorException("+ 123.3", ArgumentException.class);
        testConstructorException("- 123.3", ArgumentException.class);

        //
        // 错误的:边界:null和空字符串
        //
        testConstructorException(null, ArgumentNullException.class);
        testConstructorException("", ArgumentException.class);
        testConstructorException("null", ArgumentException.class);

        //
        // 错误的:边界:超过边界值的数字
        //
        testConstructorException(Money.Max.toString() + "999", InvalidOperationException.class);
        testConstructorException(Money.Min.toString() + "999", InvalidOperationException.class);
        testConstructorException("100000000.0000", InvalidOperationException.class);
        testConstructorException("123456789012.345678", InvalidOperationException.class);
    }

    @Test
    public void testToString()
    {
        // 测试用例要求包括以下类别: 
        // 1.输出之后最后一位经过四舍五入修正
        // 2.输出之后至少两位经过四舍五入修正(由于这个较难构造, 测试用例就免了)
        // 3.输出之后最后一位通过补零得来(相较于原数)(这个出错的可能性不大, 不测了)

        // 另外, 以上所有类别均要满足以下条件:
        // 1.包括不同精度下的输出至少一个
        // 2.输出为正或负的至少一个

        // 最后: 输出是零的单独考虑

        // 测试无关
        assertTrue(getRandomInt(0, 9) >= 0 && getRandomInt(0, 9) <= 9);

        int count = 10; // 设置10个原始数据点
        int numLength = 8; // 数字长度
        for (int i = 0; i < count; i++)
        {
            int[] numbers = new int[numLength]; // 各种数位
            numbers[0] = getRandomInt(1, 9); // 最高位, 不能是0
            for (int j = 1; j < numLength; j++)
            {
                // 其他位, 因为可能会被进位, 所以只到8
                // 因为可能需要被舍去然后四舍五入, 所以从5开始
                numbers[j] = getRandomInt(5, 8);
            }

            // 构造字符串
            String value = "";
            for (int j = 0; j < numLength; j++)
            {
                value += numbers[j];
            }
            value = value.substring(0, 3) + "." + value.substring(3);

            // 循环测试每个精度
            Money positive = new Money(value);
            Money negative = new Money("-" + value);
            for (int j = 0; j <= Money.Precision; j++)
            {
                // 构造期望值
                String expected = "";
                for (int k = 0; k < 2 + j; k++)
                {
                    expected += numbers[k];
                }
                expected += numbers[2 + j] + 1; // 最后一位被进位
                if (j != 0)
                {
                    expected = expected.substring(0, 3) + "." + expected.substring(3);
                }

                // 正数
                assertEquals("fixed=" + j + "  orginal value=" + value, expected, positive.toString(j));

                // 负数
                assertEquals("fixed=" + j + "  orginal value=-" + value, "-" + expected, negative.toString(j));
            }
        }

    }

    @Test
    public void testCompare()
    {
        // 测试用例主要包括以下内容:
        // a.大于: 正数大于正数负数和零, 零大于负数, 负数大于负数 (都要包括: 同精度比较, 高精度比低精度)
        // b.等于: 正数相等, 零相等, 负数相等 (都要包括: 同精度比较, 高精度比低精度)
        // c.小于: 负数小于负数零和正数, 零小于正数, 正数小于正数 (都要包括: 同精度比较, 高精度比低精度)
        // d.传入null: 

        // 所有需要用到的数
        String[] allString = { "888.1200", "888.0092", "56.0031", "46.001", "31.00", "28.1", "25", "19.7207", "11.349", "5.13", "4.1",
                "3", "0", "-3934", "-10964.1", "-23456.13", "-34569.349", "-55555.7207", "-666666", "-773844.1", "-812334.00",
                "-934532.001", "-999999.0031", };
        Money[] all = new Money[allString.length];
        for (int i = 0; i < all.length; i++)
        {
            all[i] = new Money(allString[i]);
        }

        // a.大于比较
        for (int i = 0; i < all.length - 1; i++)
        {
            for (int j = i + 1; j < all.length; j++)
            {
                assertTrue(all[i].toString() + "和" + all[j].toString() + "大于比较的时候发生错误", all[i].compare(all[j]) > 0);
            }
        }

        // b.相等比较
        for (int i = 0; i < all.length; i++)
        {
            Money m = new Money(all[i].toString());
            assertTrue("value=" + m.toString() + " compare=" + all[i].toString(), m.compare(all[i]) == 0);
        }

        // 零的同精度比较
        String[] zeroString = { "000", "00", "0", "0.0", "0.00", "0.000", "0.0000" };
        Money[] zeroMoney = new Money[zeroString.length];
        for (int i = 0; i < zeroMoney.length; i++)
        {
            zeroMoney[i] = new Money(zeroString[i]);
        }
        for (int i = 0; i < zeroMoney.length; i++)
        {
            for (int j = 0; j < zeroMoney.length; j++)
            {
                String message = String.format("i=%s; j=%s; ij相等比较时产生错误", zeroMoney[i].toString(), zeroMoney[j].toString());
                assertTrue(message, zeroMoney[i].compare(zeroMoney[j]) == 0);
            }
        }

        // c.小于比较
        for (int i = 0; i < all.length - 1; i++)
        {
            for (int j = i + 1; j < all.length; j++)
            {
                assertTrue(all[j].toString() + "和" + all[i].toString() + "小于比较的时候发生错误", all[j].compare(all[i]) < 0);
            }
        }

    }

    @Test
    public void testAdd()
    {
        // 测试用例见下:
        // a.正确的: 
        //   1.一般: 正数,负数,零两两相加(不同精度比较, 进位比较)
        //   2.边界: 接近或等于最大值和最小值
        // b.异常: 传入null, 超过最大值, 超过最小值

        // 所有的数
        Money m101 = new Money("55.55");
        Money m102 = new Money("712");
        Money m103 = new Money("96743.2395");
        Money m104 = new Money("22320.6937");
        Money m201 = new Money("0");
        Money m202 = new Money("00.0000");
        Money m301 = new Money("-91.3");
        Money m302 = new Money("-923.0");
        Money m303 = new Money("-568.34356");
        Money m304 = new Money("-954123.7104");

        // 正确:一般
        assertTrue(new Money("767.55").compare(m101.add(m102)) == 0); // 正数+正数
        assertTrue(new Money("55.55").compare(m101.add(m201)) == 0); // 正数+零
        assertTrue("actual=" + m101.add(m301).toString(), new Money("-35.75").compare(m101.add(m301)) == 0); // 正数+负数
        assertTrue(new Money("0").compare(m201.add(m202)) == 0); // 零+零
        assertTrue(new Money("-923.0").compare(m201.add(m302)) == 0); // 零+负数
        assertTrue(new Money("-1014.3").compare(m301.add(m302)) == 0); // 负数+负数
        assertTrue(new Money("119063.9332").compare(m103.add(m104)) == 0);
        assertTrue(new Money("-954692.054").compare(m303.add(m304)) == 0);

        // 异常
        testAddException(m102, null, ArgumentNullException.class);
        testAddException(m101, Money.Max, InvalidOperationException.class);
        testAddException(m301, Money.Min, InvalidOperationException.class);
    }

    @Test
    public void testSubtract()
    {
        // 利用add和subtract互为反运算来测试
        // 这种测试方式并不准?? testAdd挂了 testSubtract没挂??
        int count = 100;
        for (int i = 0; i < count; i++)
        {
            Money add1 = new Money(getRandomValue());
            Money add2 = new Money(getRandomValue());
            Money addResult = add1.add(add2);

            assertTrue("add1=" + add1.toString() + " add2=" + add2.toString() + "  addResult=" + addResult.toString(),
                    add1.compare(addResult.subtract(add2)) == 0);
        }
    }

    public void testOther()
    {

    }
}
